#include "CCSpineLoader.h"
#include "CCAttachment.h"
#include "CCBoneTransform.h"
#include "CCSlotTransform.h"
#include "ccTypes.h"
#include "platform/CCFileUtils.h"
#include "cJSON.h"



void CCSpineLoader::loadCurve(cJSON *frame, CCTransform::Interpolator &interpolator) {
	cJSON* curveValue = cJSON_GetObjectItem(frame, "curve");
	if (curveValue) {
		if (curveValue->type==cJSON_String && !strcmp(curveValue->valuestring, "stepped")) {
			interpolator.type = CCTransform::STEP;
			return;
		}
		else if(curveValue->type==cJSON_Array) {
			interpolator.type = CCTransform::BEZIER;
			interpolator.set(cJSON_GetArrayItem(curveValue, 0)->valuedouble, cJSON_GetArrayItem(curveValue, 1)->valuedouble,
				cJSON_GetArrayItem(curveValue, 2)->valuedouble, cJSON_GetArrayItem(curveValue, 3)->valuedouble);
			return;
		}
	}
	interpolator.type = CCTransform::LINEAR;
}

CCSkeleton* CCSpineLoader::loadSkeletonFromData(const char* data) {
	cJSON* jsonRoot = cJSON_Parse(data);

	assert(jsonRoot);

	CCSkeleton* skeleton = CCSkeleton::create();
	cJSON* bones = cJSON_GetObjectItem(jsonRoot, "bones");

	// parse bones
	int size = cJSON_GetArraySize(bones);
	for(int i = 0; i < size; i++) {
		// get key and json for this bone
		cJSON* boneItem = cJSON_GetArrayItem(bones, i);
		const char* key = cJSON_GetObjectItem(boneItem, "name")->valuestring;

		// get parent bone for this bone
		cJSON* parentItem = cJSON_GetObjectItem(boneItem, "parent");
		CCBone* parent = parentItem == NULL ? NULL : skeleton->getBone(parentItem->valuestring);

		// create this bone
		CCBone* bone = CCBone::create();

		// add to parent
		if(parent)
			parent->addChild(bone);

		// set other property
		BoneState& state = bone->getOriginalState();
		state.x = 0;
		state.y = 0;
		state.rotation = 0; 
		state.scaleX = 1;
		state.scaleY = 1;

		bone->setName(key);
		cJSON* attrLength = cJSON_GetObjectItem(boneItem, "length");
		if(attrLength) {
			bone->setLength(static_cast<float>(attrLength->valuedouble));
		}
		cJSON* attrX = cJSON_GetObjectItem(boneItem, "x");
		if(attrX) {
			state.x = static_cast<float>(attrX->valuedouble);
		}
		cJSON* attrY = cJSON_GetObjectItem(boneItem, "y");
		if(attrY) {
			state.y = static_cast<float>(attrY->valuedouble);
		}
		cJSON* attrRotation = cJSON_GetObjectItem(boneItem, "rotation");
		if(attrRotation) {
			state.rotation = static_cast<float>(attrRotation->valuedouble);
		}
		cJSON* attrScaleX = cJSON_GetObjectItem(boneItem, "scaleX");
		if(attrScaleX) {
			state.scaleX = static_cast<float>(attrScaleX->valuedouble);
		}
		cJSON* attrScaleY = cJSON_GetObjectItem(boneItem, "scaleY");
		if(attrScaleY) {
			state.scaleY = static_cast<float>(attrScaleY->valuedouble);
		}

		// add bone
		skeleton->addBone(bone);
	}

	cJSON* slots = cJSON_GetObjectItem(jsonRoot, "slots");

	// parse slots
	size = cJSON_GetArraySize(slots);
	for(int i = 0; i < size; i++) {
		cJSON* slotItem = cJSON_GetArrayItem(slots, i);
		// get key and json for this slot
		const char* key = cJSON_GetObjectItem(slotItem, "name")->valuestring;

		// get bone for this slot
		cJSON* attrBone = cJSON_GetObjectItem(slotItem, "bone");

		// create slot
		CCSlot* slot = CCSlot::create();

		CCBone* bone = skeleton->getBone(attrBone->valuestring);

		bone->addSlot(slot);

		// set name
		slot->setName(key);

		// set color
		SlotState& state = slot->getOriginalState();

		cJSON* attrColor = cJSON_GetObjectItem(slotItem, "color");

		if(attrColor) {
			char* buf=attrColor->valuestring;
			state.color = getColorforStr(buf);
		}
		/*
		else {
		state.color = ccc4(255,255,255,255);
		}*/

		// set active skin attachment name
		cJSON* attrAttachment = cJSON_GetObjectItem(slotItem, "attachment");
		if(attrAttachment) {
			state.activeAttachmentName = attrAttachment->valuestring;
		}

		cJSON* attrAdditive = cJSON_GetObjectItem(slotItem, "additive");

		if (attrAdditive) {
			state.additive=true;
		}

		// add slot
		skeleton->addSlot(slot);
	}

	// parse skin attachment
	cJSON* skins = cJSON_GetObjectItem(jsonRoot, "skins");
	size = cJSON_GetArraySize(skins);
	for(int i = 0; i < size; ++i) {
		cJSON* skinItem = cJSON_GetArrayItem(skins, i);
		// create skin
		CCSkin* skin = CCSkin::create();
		skin->setName(skinItem->string);

		// parse this skin
		int slotSize = cJSON_GetArraySize(skinItem);
		for(int j = 0; j < slotSize; j++) {
			// get slot
			cJSON* slotItem = cJSON_GetArrayItem(skinItem, j);
			CCSlot* slot = skeleton->getSlot(slotItem->string);
			if(!slot)
				continue;

			// get json of attachments
			int attSize = cJSON_GetArraySize(slotItem);

			// parse every attachment
			for(int k = 0; k < attSize; k++) {
				// get attachment name
				cJSON* attachmentItem = cJSON_GetArrayItem(slotItem, k);

				// create skin attachment
				CCAttachment* skinAtt = CCAttachment::create();
				skinAtt->setName(attachmentItem->string);
				skinAtt->setPath(cJSON_GetObjectItem(attachmentItem, "name")?cJSON_GetObjectItem(attachmentItem, "name")->valuestring:attachmentItem->string);

				// set other property
				skinAtt->setX(static_cast<float>(cJSON_GetObjectItem(attachmentItem, "x")?cJSON_GetObjectItem(attachmentItem, "x")->valuedouble:0.0f));
				skinAtt->setY(static_cast<float>(cJSON_GetObjectItem(attachmentItem, "y")?cJSON_GetObjectItem(attachmentItem, "y")->valuedouble:0.0f));
				skinAtt->setRotation(static_cast<float>(cJSON_GetObjectItem(attachmentItem, "rotation")?cJSON_GetObjectItem(attachmentItem, "rotation")->valuedouble:0.0f));
				skinAtt->setScaleX(static_cast<float>(cJSON_GetObjectItem(attachmentItem, "scaleX")?cJSON_GetObjectItem(attachmentItem, "scaleX")->valuedouble:1.0f));
				skinAtt->setScaleY(static_cast<float>(cJSON_GetObjectItem(attachmentItem, "scaleY")?cJSON_GetObjectItem(attachmentItem, "scaleY")->valuedouble:1.0f));
				//TODO  width height
				//skinAtt->setWidth(static_cast<float>(cJSON_GetObjectItem(attachmentItem, "width")->valuedouble));
				//skinAtt->setHeight(static_cast<float>(cJSON_GetObjectItem(attachmentItem, "height")->valuedouble));

				// add attachment
				skin->addSlot(slotItem->string, skinAtt);

				//printf("%s\n",skinAtt->getPath());
			}

			//printf("@@@@ %s\n",slot->getName());
		}

		// add skin to skeletaon
		skeleton->addSkin(skin);
	}

	cJSON* animations = cJSON_GetObjectItem(jsonRoot, "animations");
	size = cJSON_GetArraySize(animations);
	for(int i = 0; i < size; ++i) {
		cJSON* item = cJSON_GetArrayItem(animations, i);
		const char* animName = item->string;
		CCSkeletalAnimation* anim = loadAnimation(item,skeleton);
		anim->setName(animName);
		skeleton->addAnim(anim);
	}

	// return
	return skeleton;
}

CCSkeletalAnimation* CCSpineLoader::loadAnimation(cJSON* object,CCSkeleton* skeleton) {
	CCSkeletalAnimation* anim = CCSkeletalAnimation::create();
	cJSON* jsonRoot = object;
	// duration time
	float duration = 0;

	// get bones object
	cJSON* bones = cJSON_GetObjectItem(jsonRoot, "bones");
	if(bones) {
		// iterate every bone
		cJSON* boneItem = bones->child;
		for(; boneItem; boneItem = boneItem->next) {
			// get bone key and bone transform json object

			// create transform
			CCBoneTransform* bt = CCBoneTransform::create(skeleton->getBone(boneItem->string));

			//printf("%s\n",boneItem->string);

			// get rotation key frames
			cJSON* attrRotate = cJSON_GetObjectItem(boneItem, "rotate");
			if(attrRotate) {
				int count = cJSON_GetArraySize(attrRotate);
				for(int j = 0; j < count; j++) {
					// get time and angle
					cJSON* rotateKeyFrameValue = cJSON_GetArrayItem(attrRotate, j);
					CCBoneTransform::RotationKeyFrame kf;
					kf.time = static_cast<float>(cJSON_GetObjectItem(rotateKeyFrameValue, "time")->valuedouble);
					kf.angle = static_cast<float>(cJSON_GetObjectItem(rotateKeyFrameValue, "angle")->valuedouble);

					// save max time as duration
					duration = max(duration, kf.time);

					// get interpolator
					loadCurve(rotateKeyFrameValue,kf.interpolator);

					// add key frame
					bt->addRotationKeyFrame(kf);
				}
			} // end if(rkfListJO)

			// get translation key frames
			cJSON* attrTranslate = cJSON_GetObjectItem(boneItem, "translate");
			if(attrTranslate) {
				int count = cJSON_GetArraySize(attrTranslate);
				for(int j = 0; j < count; j++) {
					// get time, x and y
					cJSON* translateKeyFrameValue = cJSON_GetArrayItem(attrTranslate, j);
					CCBoneTransform::TranslationKeyFrame kf;
					kf.time = static_cast<float>(cJSON_GetObjectItem(translateKeyFrameValue, "time")->valuedouble);
					kf.x = static_cast<float>(cJSON_GetObjectItem(translateKeyFrameValue, "x")->valuedouble);
					kf.y = static_cast<float>(cJSON_GetObjectItem(translateKeyFrameValue, "y")->valuedouble);

					// save max time as duration
					duration = max(duration, kf.time);

					// get interpolator
					loadCurve(translateKeyFrameValue,kf.interpolator);

					// add key frame
					bt->addTranslationKeyFrame(kf);
				}
			} // end if(tkfListJO)

			// get scale key frames
			cJSON* attrScale = cJSON_GetObjectItem(boneItem, "scale");
			if(attrScale) {
				int count = cJSON_GetArraySize(attrScale);
				for(int j = 0; j < count; j++) {
					// get time, x and y
					cJSON* scaleKeyFrameValue = cJSON_GetArrayItem(attrScale, j);
					CCBoneTransform::ScaleKeyFrame kf;
					kf.time = static_cast<float>(cJSON_GetObjectItem(scaleKeyFrameValue, "time")->valuedouble);
					kf.scaleX = static_cast<float>(cJSON_GetObjectItem(scaleKeyFrameValue, "x")->valuedouble);
					kf.scaleY = static_cast<float>(cJSON_GetObjectItem(scaleKeyFrameValue, "y")->valuedouble);

					// save max time as duration
					duration = max(duration, kf.time);

					// get interpolator
					loadCurve(scaleKeyFrameValue,kf.interpolator);

					// add key frame
					bt->addScaleKeyFrame(kf);
				}
			} // end if(skfListJO)

			// add transform
			anim->addTransform(bt);
		} // end for
	} // end if(bonesJO)

	// get slots json
	cJSON* slots = cJSON_GetObjectItem(jsonRoot, "slots");
	if(slots) {
		// iterate every slot
		int size = cJSON_GetArraySize(slots);
		for(int i = 0; i < size; i++) {
			// get slot key and slot transform json object
			cJSON* slotItem = cJSON_GetArrayItem(slots, i);

			// create transform
			CCSlotTransform* st = CCSlotTransform::create(skeleton->getSlot(slotItem->string));

			// get slot skin key frames
			cJSON* attrAttachment = cJSON_GetObjectItem(slotItem, "attachment");
			if(attrAttachment) {
				int count = cJSON_GetArraySize(attrAttachment);
				for(int j = 0; j < count; j++) {
					// get time and skin name
					cJSON* attachmentKeyFrameValue = cJSON_GetArrayItem(attrAttachment, j);
					CCSlotTransform::SkinKeyFrame kf;
					kf.time = static_cast<float>(cJSON_GetObjectItem(attachmentKeyFrameValue, "time")->valuedouble);
					kf.attachmentName = cJSON_GetObjectItem(attachmentKeyFrameValue, "name")->valuestring;

					// save max time as duration
					duration = max(duration, kf.time);

					// interpolator for skin transform is always STEP
					kf.interpolator.type = CCTransform::STEP;

					// add key frame
					// addSkinKeyFrame doesn't copy skin name, so no need to release it
					st->addSkinKeyFrame(kf);
				}
			}

			//printf("slot : %s\n",slotItem->string);
			// get slot color key frame
			cJSON* attrColor = cJSON_GetObjectItem(slotItem, "color");
			if(attrColor) {
				int count = cJSON_GetArraySize(attrColor);
				for(int j = 0; j < count; j++) {
					// get time and color
					cJSON* colorKeyFrameValue = cJSON_GetArrayItem(attrColor, j);
					CCSlotTransform::ColorKeyFrame kf;
					kf.time = static_cast<float>(cJSON_GetObjectItem(colorKeyFrameValue, "time")->valuedouble);
					cJSON* colorItem = cJSON_GetObjectItem(colorKeyFrameValue, "color");
					if(colorItem) {
						char* buf=colorItem->valuestring;
						kf.color=getColorforStr(buf);
					}
					else {
						kf.color = Color4B(255,255,255,255);
					}

					// save max time as duration
					duration = max(duration, kf.time);

					// interpolator for color transform is always LINEAR
					kf.interpolator.type = CCTransform::LINEAR;


					// add key frame
					st->addColorKeyFrame(kf);
				}
			}

			// add transform
			anim->addTransform(st);
		} // end for
	} // end if(slotsJO)

	// set duration
	anim->m_duration=duration;

	// parse events
	cJSON* events = cJSON_GetObjectItem(jsonRoot, "events");
	if(events) {
		int size = cJSON_GetArraySize(events);
		for(int i = 0; i < size; ++i) {
			cJSON* item = cJSON_GetArrayItem(events, i);
			float t = static_cast<float>(cJSON_GetObjectItem(item, "time")->valuedouble);
			const char* s = cJSON_GetObjectItem(item, "name")->valuestring;
			EventData* e = new EventData(t, s);

			anim->addEvent(e);
		}
	}

	// return
	return anim;
}

Color4B CCSpineLoader::getColorforStr(const char *str) {
	static map<char, int> mp;
	for (int i=0;i<10; i++) {
		mp['0'+i]=i;
	}
	for (int i=10;i<16; i++) {
		mp['a'+i-10]=i;
	}
	GLubyte res[4]={0};
	for (int i=0; i<4; i++) {
		int a=mp[str[i<<1]];
		int b=mp[str[i<<1|1]];
		res[i]=(a<<4)+b;
	}
	return Color4B(res[0],res[1],res[2],res[3]);
}

CCSkeleton* CCSpineLoader::loadSkeleton(const char* path) {
	unsigned long length = 0;
	char* data = (char*)CCFileUtils::getInstance()->getFileData(path, "rb", &length);
	if(data) {
		CCSkeleton* skeleton = loadSkeletonFromData(data);
		delete[] data;
		return skeleton;
	}

	return nullptr;
}


